/*
*****************
** TP2: Tetris **
* Cyril LAIGNEL *
*** Info 523 ****
*****************

***********************************************************************************************
/!\ REMARQUE : L'entrée utlisateur peut générer un bug (boucle infinie) si le caratère saisi
n'est pas un chiffre. J'ai regardé une solution possible pour vérifier le type d'entrée d'une
variable mais cette solution faisait appel a la fonction 'cin' qui est du C++.
Ce bug est donc encore présent pour ma version finale...
*********************************************************************************************** 
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "tetris.h"

#define NB_PIECES 5
#define HAUTEUR_MAX_DES_PIECES 4



/* Procédure qui remplit un tableau de pièces avec des pièces de jeu */

void genererPieces(tabPieces  lesPieces)
{
	/* Initialisation de la piece
 
	 @
	@@@
	
	*/
	lesPieces->hauteur = 2;
	lesPieces->largeur = 3;
	lesPieces->forme[1] = " @ ";
	lesPieces->forme[0] = "@@@";
	lesPieces++;

	/* Initialisation de la piece

	&
	&
	&
	&

	*/
	lesPieces->hauteur = 4;
	lesPieces->largeur = 1;
	lesPieces->forme[3] = "&";
	lesPieces->forme[2] = "&";
	lesPieces->forme[1] = "&";
	lesPieces->forme[0] = "&";
	lesPieces++;

	/* Initialisation de la piece

	$$$$

	*/
	lesPieces->hauteur = 1;
	lesPieces->largeur = 4;
	lesPieces->forme[0] = "$$$$";
	lesPieces++;
	
	/* Initialisation de la piece

	?  
	?   
	?  
	???

	*/
	lesPieces->hauteur = 4;
	lesPieces->largeur = 3;
	lesPieces->forme[3] = "?  ";
	lesPieces->forme[2] = "?  ";
	lesPieces->forme[1] = "?  ";
	lesPieces->forme[0] = "???";
	lesPieces++;
	
	/* Initialisation de la piece

	##
	##
	
	*/
	lesPieces->hauteur = 2;
	lesPieces->largeur = 2;
	lesPieces->forme[1] = "##";
	lesPieces->forme[0] = "##";
	lesPieces++;

}

/* Procédure qui initialise une grille de jeu */

void initialiseGrille(Grille tabGrille)
{
	int i, j;
	for (i=0;i<HAUTEUR;i++)
	{
		for (j=0;j<LARGEUR;j++)
		{
			tabGrille[i][j] = ' ';
		}
	}
}


/* Fonction qui permet de lire le contenu d'une case de la grille de jeu */

char lireCase(Grille tabGrille, int ligne, int col)
{
	return tabGrille[ligne][col];
}


/* Procédure qui affiche la grille de jeu (l'espace grille étant délimité par des caractères pipes) ainsi que le numéro des colonnes */

void afficheGrille(Grille tabGrille)
{
	int i, j, incr;
	
	/* Boucle qui affiche ligne par ligne la grille de jeu, encadrée par des double pipes */
	for (i=0;i<HAUTEUR;i++)
	{
		printf("||");
		for (j=0;j<LARGEUR;j++)
		{
			printf("%c", tabGrille[i][j]);
		}
		printf("||\n");
	}
	
	/* Boucle qui affiche la limite en bas de la grille de jeu (avec des pipes) en fonction de la largeur précisée lors du #define LARGEUR */
	printf("||");
	for (j=0;j<LARGEUR;j++)
	{
		printf("|");
	}
	printf("||\n");

	/* Découpe la valeur des colonnes par dizaines */
	printf("  "); /* permet de faire correspondre les numéros de colonnes avec les pipes */
	for (j=0;j<LARGEUR;j++)
	{
		if (j%10 == 0)
			printf("%i", j/10);
		else
			printf(" ");
	}
	printf("\n");
	
	/* Affiche les unités des colonnes correspondantes */
	printf("  "); /* permet de faire correspondre les numéros de colonnes avec les pipes */
	incr = 0;
	for (j=0;j<LARGEUR;j++)
	{
		if (incr%10 == 0)
			incr = 0;
		printf("%i", incr);
		incr ++;
	}
	printf("\n");	
}

/* Procédure qui permet d'afficher la pièce placée en haut de la grille de jeu */

void afficherPiece(Piece p) 
{
	int i,j,decalage;
	printf("\n");
	decalage = 2 + (LARGEUR - p.largeur) / 2;
	for (i=p.hauteur-1; i>=0; --i) 
	{
		for ( j=0; j < decalage; ++j )
		    printf(" ");
		for ( j=0; j<p.largeur; j++ ) 
		    printf("%c",p.forme[i][j]);
		printf("\n");
	}
	for ( j=0; j < decalage; ++j )
		printf(" ");
	printf("↑\n");
}


/* Procédure qui permet d'écrire un caractère dans le tableau grille */

void ecrireCase(Grille tabGrille, int ligne, int col, char car)
{
	tabGrille[ligne][col] = car; 
}


/* Fonction qui retourne la valeur de la hauteur maximale dans un intervalle de colonne donné, sinon -1. */

int hauteurMax(Grille tabGrille, int debut_col, int fin_col)
{
	int i, j, max = HAUTEUR;
	for(i=0;i<HAUTEUR;i++)
	{
		for(j=debut_col;j<fin_col;j++)
		{
			if(tabGrille[i][j] != ' ' && i<max)
				max = i;
		}
	}
	if(max != 0)
		return max;
	return -1;
}


/* Procédure qui permet d'écrire une pièce dans la grille de jeu à un emplacement passé en paramètre. */

void ecrirePiece(Grille tabGrille, Piece p, int col, int hauteur)
{
	int i, j;
	if (hauteur == -1)
		hauteur = HAUTEUR-1;
	else
		--hauteur;
	for ( i=0;i<p.hauteur;i++)
	{
		for (j=0;j<p.largeur;j++)
		{	
			ecrireCase(tabGrille, hauteur-i, col+j, p.forme[i][j]);
		}
	}
}


/* Fonction qui génère une pièce aléatoirement */

Piece pieceAleatoire(tabPieces lesPieces)
{
	int alea = (int)(rand()/(float)RAND_MAX * (NB_PIECES));
	return lesPieces[alea];
}


/* Fonction qui vérifie si le joueur a perdu (= dernière ligne de la grille de jeu atteinte) */

int partiePerdu(Piece p, int hauteur)
{
	if (p.hauteur > hauteur)
		return 1;
	else
		return 0;
}

/* Procédure qui supprime une ligne passée en paramètre et qui décale le reste du jeu en conséquence */

void supprimerLigne(Grille tabGrille, int ligne)
{
	int i, j;	
	for(i=ligne;i>0;i--)
	{
		for(j=0;j<LARGEUR;j++)
			tabGrille[i][j] = tabGrille[i-1][j];
	}
	for(j=0;j<LARGEUR;j++)
		tabGrille[i][j] = ' ';
}


/* Procédure qui supprime les lignes complètes (= sans caractère espace dedans) */

void nettoyer(Grille tabGrille)
{
	int i,j,cmpt;
	for(i=0;i<HAUTEUR;i++)
	{
		cmpt = 0;
		for(j=0;j<LARGEUR;j++)
		{
			if(tabGrille[i][j] != ' ')
				cmpt ++;
		}
		if(cmpt == LARGEUR)
			supprimerLigne(tabGrille, i);
	}
}

/* Procédure qui remplit un tableau passé en paramètre (de taille LARGEUR) avec toutes les valeurs possibles de la largeur de la grille */

void rempliTabLargeur(int tabLargeur[])
{
	int i;
	for(i=0;i<LARGEUR;i++)
	{
		tabLargeur[i] = i;
	}
}

/* Fonction qui retourne 1 si la valeur 'entree' existe dans le tableau ET ne dépasse pas la taille de la largeur de la pièce avec celle de la grille */

int verifEntree(int tabLargeur[], int entree, Piece p)
{
	int i, val_ret = 0;
	for(i=0;i<LARGEUR;i++)
	{
		if(tabLargeur[i] == entree && entree <= LARGEUR-p.largeur)
			val_ret = 1;
	}
	return val_ret;
}


/* Fonction principale qui instancie une partie et gère le fonctionnement principal du jeu */

int main_console()
{
	int col = 0, fin_partie = 0;
	Grille tabGrille;
	tabPieces pieces;
	Piece p;
	int tabLargeur[LARGEUR];
	srand(time(NULL)); /* améliore la fonction aléatoire "rand" */
	initialiseGrille(tabGrille);
	genererPieces(pieces);
	rempliTabLargeur(tabLargeur);
	while (col != -1)
	{
		/* Ré-initialise une grille de jeu si la partie est terminée */
		if (fin_partie == 1)
		{
			fin_partie = 0;
			col = 0;
			printf("La partie recommence, bonne chance :)\n");
			initialiseGrille(tabGrille);
		}
		
		p = pieceAleatoire(pieces);
		afficherPiece(p);
		afficheGrille(tabGrille);
		printf("Indiquez le numéro de la colonne souhaitée :\n");
		scanf("%d",&col);
		fflush(stdin);
		fin_partie = partiePerdu(p, hauteurMax(tabGrille, col, col+p.largeur));
		
		/* Boucle qui vérifie si la colonne indiqué est comprise dans la largeur du tableau ET si la largeur de la pièce ne dépasse pas la limite droite de la grille de jeu */
		if(verifEntree(tabLargeur, col, p) == 1)
		{
			/* Taitement de l'ajout d'une pièce dans la grille de jeu si le joueur n'a pas atteint le sommet (= si le joueur n'a pas perdu) */
			if(col != -1 && fin_partie != 1)
			{
				ecrirePiece(tabGrille, p, col, hauteurMax(tabGrille, col, col+p.largeur));
				nettoyer(tabGrille);
				afficheGrille(tabGrille);
			}
			
			/* Message de fin de partie quand partie perdue */
			if (fin_partie == 1)
			{
				printf("Vous avez perdu !\n\n");
			}
		}
	}
	return 0;
}

void jouer(Jeu jeu)
{
	int col = 0, fin_partie = 0;
	tabPieces pieces;
	Piece p;
	int tabLargeur[LARGEUR];
	srand(time(NULL)); /* améliore la fonction aléatoire "rand" */
	genererPieces(pieces);
	rempliTabLargeur(tabLargeur);
	while (col != -1)
	{
		/* Ré-initialise une grille de jeu si la partie est terminée */
		if (fin_partie == 1)
		{
			fin_partie = 0;
			col = 0;
			printf("La partie recommence, bonne chance :)\n");
			initialiseGrille(jeu.g);
		}
		
		p = pieceAleatoire(pieces);
		afficherPiece(p);
		dessineGrille(jeu.g);
		printf("Indiquez le numéro de la colonne souhaitée :\n");
		scanf("%d",&col);
		fflush(stdin);
		fin_partie = partiePerdu(p, hauteurMax(jeu.g, col, col+p.largeur));
		
		/* Boucle qui vérifie si la colonne indiqué est comprise dans la largeur du tableau ET si la largeur de la pièce ne dépasse pas la limite droite de la grille de jeu */
		if(verifEntree(tabLargeur, col, p) == 1)
		{
			/* Taitement de l'ajout d'une pièce dans la grille de jeu si le joueur n'a pas atteint le sommet (= si le joueur n'a pas perdu) */
			if(col != -1 && fin_partie != 1)
			{
				ecrirePiece(jeu.g, p, col, hauteurMax(jeu.g, col, col+p.largeur));
				nettoyer(jeu.g);
				dessineGrille(jeu.g);
			}
			
			/* Message de fin de partie quand partie perdue */
			if (fin_partie == 1)
			{
				printf("Vous avez perdu !\n\n");
			}
		}
	}
}

